package game.physics;

import java.awt.Point;


/**
 * This is a vector class to make the physics of movement in the game easier.
 * 
 * @author Ben Schwab
 * @author Paul Dannenberg
 * 
 */

public class GameVector {

    private double myX;
    private double myY;

    /**
     * 
     * @param x x value of vector
     * @param y y value of vector
     */
    public GameVector(double x, double y) {
        myX = x;
        myY = y;
    }

    /**
     * Create a vector between two points.
     * 
     * @param one - tail of vector
     * @param two - head of vector
     */
    public GameVector(Point tail, Point head) {
        myX = head.getX() - tail.getX();
        myY = head.getY() - tail.getY();
    }

    /**
     * Creates a new vector that is an identical
     * copy of another vector.
     * 
     * @param gameVector The GameVector to copy.
     */
    public GameVector(GameVector gameVector) {
        myX = gameVector.myX;
        myY = gameVector.myY;
    }

    /**
     * Multiply the vector by a constant
     * 
     * @param c Constant to multiply vector by
     */
    public void multiply(double c) {
        myX *= c;
        myY *= c;
    }

    /**
     * Add another vector to this vector.
     * 
     * @param other Vector to add this vector.
     */
    public void combineVector(GameVector other) {
        myX += other.getX();
        myY += other.getY();
    }

    /**
     * Returns a new vector that is a combination of the two parameter vectors
     * 
     * @param one first vector to combine
     * @param two second vector to combine
     * @return a new vector that is a combination of one and two
     */
    public static GameVector combineVector(GameVector one, GameVector two) {
        return new GameVector(one.getX() + two.getX(), one.getY() + two.getY());
    }

    /**
     * 
     * @return returns the magnitude of the vector
     */
    public double getMagnitude() {
        return Math.pow(myX * myX + myY * myY, 0.5);

    }

    /**
     * 
     * @return return the x coordinate of the vector
     */
    public double getX() {
        return myX;
    }

    /**
     * 
     * @param x value to set vector
     */
    public void setX(double x) {
        myX = x;
    }

    /**
     * 
     * @return y value of vector
     */
    public double getY() {
        return myY;
    }

    /**
     * 
     * @param y y value to set vector
     */
    public void setY(double y) {
        myY = y;
    }

    /**
     * Compares the magnitude of two vectors
     * 
     * @param other The vector that this vector should be compared to.
     * @return 1 if this vector is greater than the other vector, 0 if the same,
     *         -1 if smaller
     */
    public int compareTo(GameVector other) {
        if (getMagnitude() - other.getMagnitude() == 0) {
            return 0;
        } else if (getMagnitude() - other.getMagnitude() >= 0) {
            return 1;
        } else {
            return -1;
        }

    }

    /**
     * Computes the dot product between two vectors
     * 
     * @param one first vector
     * @param two second vector
     * @return dot product between vectors
     */
    public static double dotProduct(GameVector one, GameVector two) {
        return one.getX() * two.getX() + one.getY() * two.getY();

    }

    /**
     * 
     * @param other vector to compute dot product with
     * @return the dot product of this vector with another vector
     */
    public double dotProduct(GameVector other) {
        return getX() * other.getX() + getY() * other.getY();

    }

    /**
     * @param one first vector
     * @param two second vector
     * @return Returns the smallest angle between two vectors
     */
    public static double getAngle(GameVector one, GameVector two) {
        double tempAngle = Math.acos(GameVector.dotProduct(one, two)
                / (one.getMagnitude() * two.getMagnitude()));
        return tempAngle;

    }

    /**
     * 
     * @return returns the unit vector this vector
     */
    public GameVector getUnitVector() {
        return new GameVector(myX / getMagnitude(), myY / getMagnitude());
    }

    /**
     * 
     * @return returns the direction of this angle
     */
    public double getDirectionAngle() {

        double tempAngle = Math.atan2(-myY, myX);
        tempAngle = Math.toDegrees(tempAngle);
        if (myY > 0) {
            tempAngle += 360;
        }

        return tempAngle;
    }

    /**
     * Inverts the vector's direction while keeping
     * its magnitude the same.
     */
    public void negate() {
        myX = -myX;
        myY = -myY;
    }

    /**
     * Projects this GameVector onto another game
     * vector.
     * 
     * @param other The vector that this vector will be
     *        projected onto.
     */
    public void project(GameVector other) {
        double projectedMagnitude = this.dotProduct(other)
                / Math.pow(other.getMagnitude(), 2);
        myX = projectedMagnitude * other.myX;
        myY = projectedMagnitude * other.myY;
    }

    /**
     * Rotates the GameVector counterclockwise.
     * 
     * @param angleInDegrees The angle to rotate the GameVector.
     */
    public void rotate(double angleInDegrees) {
        double angleInRadians = Math.toRadians(angleInDegrees);
        myX = myX * Math.cos(angleInRadians) - myY * Math.sin(angleInRadians);
        myY = myX * Math.sin(angleInRadians) + myY * Math.cos(angleInRadians);
    }

}
